﻿using System;
using System.Collections.Generic;

using Layers;
using System.Text;

namespace HoldemGame
{
    public static class HoldemActionID
    {
        public const int START = 10;    // game starts
        public const int PREFLOP = 11;  // cards given, preflop betting
        public const int FLOP = 12;     // flop opened
        public const int TURN = 13;     // turn opened
        public const int RIVER = 14;    // river opened
        public const int SHOW = 15;     // player shaw his cards to opponents
        public const int FINAL = 16;    // the winner(s) is known


        public const int FOLD = 20;
        public const int RAISE = 21;
        public const int CHECK = 22;
        public const int CALL = 23;
        public const int BLIND = 24;
        public const int WIN = 25;
        public const int CARDS = 30;    // player get pocket cards
        public const int BOARDCARDS = 31;

        public const int CURRENT = 40;
        public const int FIRST = 41;
        public const int SWITCHFIRST = 42;

        public const int SITDOWN = 50;  // initially sit down
        public const int STANDUP = 51;  // totally quit
        public const int SITOUT = 52;   // temp sit out
        public const int RETURN = 53;   // return after sit out

        public static string ToString(int id)
        {
            switch(id)
            {
                case START: return "START";
                case PREFLOP: return "PREFLOP";
                case FLOP: return "FLOP";
                case TURN: return "TURN";
                case RIVER: return "RIVER";
                case SHOW: return "SHOW";
                case FINAL: return "FINAL";
                case WIN: return "WIN";
                case FOLD: return "FOLD";
                case RAISE: return "RAISE";
                case CHECK: return "CHECK";
                case CALL: return "CALL";
                case BLIND: return "BLIND";
                case CARDS: return "CARDS";
                case BOARDCARDS: return "BOARDCARDS";

                case CURRENT: return "CURRENT";
                case FIRST: return "FIRST";
                case SWITCHFIRST: return "SWITCHFIRST";

                case SITDOWN: return "SITDOWN";
                case STANDUP: return "STANDUP";
                case SITOUT: return "SITOUT";
                case RETURN: return "RETURN";
            }
            throw new InvalidOperationException();
        }
    }

    public abstract class HoldemAction
    {
        public string GameID{get;set;}
        public int ID{get;set;}

        //public IGameServer Server {get;set;}

        public int Step;
        public int RunIndex;

        public static int CurrentRunIndex;

        public abstract void Do(IGameServer server);

        public static T CreateAction<T>(int id, IGameServer srv) where T : HoldemAction, new()
        {
            T action = new T();
            //action.Server = srv;
            action.ID = id;
            action.GameID = srv.GameID;
            action.RunIndex = CurrentRunIndex;
            action.Step = srv.Step;
            return action;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(HoldemActionID.ToString(ID));
            return sb.ToString();
        }

    }

    public abstract class PlayerAction : HoldemAction
    {
        public string PlayerID { get; set; }
        public GamePlayer Player;

        //public GamePlayer GetPlayer(IGameState game)
        //{
        //    return game.Players.Find(PlayerID);
        //}

        public static T CreateAction<T>(int id, IGameServer srv, GamePlayer p) where T : PlayerAction, new()
        {
            T action = CreateAction<T>(id, srv);
            action.PlayerID = p.ID;
            action.Player = p;
            return action;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(base.ToString());
            if (null != PlayerID)
                sb.AppendFormat(" {0}", PlayerID);
            return sb.ToString();
        }

    }


    public abstract class BetAction : PlayerAction
    {
        public double? Amount { get; set; }

        public double BankDelta
        {
            get
            {
                if (ID == HoldemActionID.WIN)
                    return Amount.Value;
                return -Amount.Value;
            }
        }

        public static T CreateAction<T>(int id, IGameServer game, GamePlayer p, double amount) where T:BetAction,new()
        {
            T ra = CreateAction<T>(id, game, p);
            ra.Amount = amount;
            return ra;
        }

        
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(base.ToString());

            if (null != Amount)
                sb.AppendFormat(" {0}", Amount);
            return sb.ToString();
        }
    }

    public class CallAction : BetAction
    {
        public override void Do(IGameServer srv)
        {
            srv.Call();
        }

        public static CallAction Create(int id, IGameServer game, GamePlayer p, double amount)
        {
            CallAction ca = CreateAction<CallAction>(id, game, p, amount);
            return ca;
        }
    }

    public class RaiseAction : BetAction
    {
        public override void Do(IGameServer srv)
        {
            srv.Raise();
        }
        
        public static RaiseAction Create(int id, IGameServer game, GamePlayer p, double amount)
        {
            RaiseAction ca = CreateAction<RaiseAction>(id, game, p, amount);
            return ca;
        }
    }

    public class WinAction : BetAction
    {
        public override void Do(IGameServer srv)
        {
            srv.Win(Player, Amount.Value);
        }

        public static WinAction Create(int id, IGameServer game, GamePlayer p, double amount)
        {
            WinAction ca = CreateAction<WinAction>(id, game, p, amount);
            return ca;
        }
    }

    public class BlindAction : BetAction
    {
        public override void Do(IGameServer srv)
        {
            srv.Blind();
        }

        public static BlindAction Create(int id, IGameServer game, GamePlayer p, double amount)
        {
            BlindAction a = CreateAction<BlindAction>(id, game, p, amount);
            return a;
        }
    }


  

    public class FoldAction : PlayerAction
    {
        public override void Do(IGameServer srv)
        {
            srv.Fold();
        }

        public static FoldAction Create(IGameServer game, GamePlayer p)
        {
            FoldAction a = CreateAction<FoldAction>(HoldemActionID.FOLD, game, p);
            return a;
        }
    }

    public class PlayerPositionAction : PlayerAction
    {
        public override void Do(IGameServer srv)
        {
            if (HoldemActionID.CURRENT.Equals(ID))
                srv.SetCurrent(Player);
            else if (HoldemActionID.FIRST.Equals(ID))
                srv.SetFirst(Player);
            else if (HoldemActionID.SWITCHFIRST.Equals(ID))
                srv.SwitchFirst();
            else
                throw new InvalidOperationException();
        }

        public static PlayerPositionAction Create(int id, IGameServer game, GamePlayer p)
        {
            PlayerPositionAction a = CreateAction<PlayerPositionAction>(id, game, p);
            return a;
        }
    }

    public abstract class CardsAction : PlayerAction
    {
        protected CardSet cset;

        public static T Create<T>(IGameServer game, GamePlayer p, CardSet cset) where T:CardsAction,new()
        {
            T a = CreateAction<T>(HoldemActionID.CARDS, game, p);
            a.cset = cset;
            a.cset.CopyFrom(cset);
            return a;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(base.ToString());
            sb.AppendFormat(" {0}", cset.ToString());
            return sb.ToString();
        }
    }

    public class PocketCardsAction : CardsAction
    {
        public override void Do(IGameServer srv)
        {
            srv.PocketCards(Player, cset);
        }

        public static PocketCardsAction Create(IGameServer game, GamePlayer p, CardSet cset)
        {
            return Create<PocketCardsAction>(game, p, cset);
        }
    }

   

    public class BoardCardsAction : HoldemAction
    {
        private CardSet cset;

        public override void Do(IGameServer srv)
        {
            if (HoldemActionID.FLOP.Equals(ID))
                srv.PostFlop(cset);
            else if (HoldemActionID.TURN.Equals(ID))
                srv.PostTurn(cset);
            else if (HoldemActionID.RIVER.Equals(ID))
                srv.PostRiver(cset);
            else if (HoldemActionID.BOARDCARDS.Equals(ID))
                srv.BoardCards(cset);
            else
                throw new InvalidOperationException();
        }

        public static BoardCardsAction Create(int id, IGameServer game, CardSet board)
        {
            BoardCardsAction a = CreateAction<BoardCardsAction>(id, game);

            a.cset=board.Clone();

            return a;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(base.ToString());
            sb.AppendFormat(" {0}", cset.ToString());
            return sb.ToString();
        }
    }

    public class StageAction : HoldemAction
    {
        public override void Do(IGameServer srv)
        {
            if (HoldemActionID.PREFLOP.Equals(ID))
                srv.PreFlop();
            else if (HoldemActionID.FINAL.Equals(ID))
                srv.Final();
            else if (HoldemActionID.START.Equals(ID))
                srv.NewGame();
            else
                throw new InvalidOperationException();
        }

        public static StageAction Create(int id, IGameServer game)
        {
            StageAction a = CreateAction<StageAction>(id, game);
            return a;
        }

    }

    public class PlayerStateAction : PlayerAction
    {
        public override void Do(IGameServer srv)
        {

            switch (ID)
            {
                case HoldemActionID.SHOW: srv.ShowCards(Player); break;
                default: throw new InvalidOperationException();
            }
        }

        public static PlayerStateAction Create(int id, IGameServer game, GamePlayer p)
        {
            PlayerStateAction a = CreateAction<PlayerStateAction>(id, game, p);
            return a;
        }
    }

    public class HoldemActionEventArgs : EventArgs
    {
        public HoldemAction Action {get;set;}
        public IGameServer Game {get;set;}

        public HoldemActionEventArgs(HoldemAction a, IGameServer game)
        {
            Action = a;
            Game = game;
        }
    }
}